package com.lanchetech.user.service.impl;


import com.alibaba.excel.util.CollectionUtils;
import com.baidu.fsg.uid.UidGenerator;
import com.github.binarywang.wxpay.bean.notify.WxPayNotifyResponse;
import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.lanchetech.bean.request.AnchorRankReq;
import com.lanchetech.bean.request.AnchorWindowsReq;
import com.lanchetech.bean.request.DepositReq;
import com.lanchetech.bean.request.PartnerReq;
import com.lanchetech.bean.response.*;
import com.lanchetech.bean.util.PageHelpUtil;
import com.lanchetech.bean.vo.*;
import com.lanchetech.common.enums.*;
import com.lanchetech.common.util.IpUtil;
import com.lanchetech.dao.*;
import com.lanchetech.entity.*;
import com.lanchetech.service.DaoService;
import com.lanchetech.service.LevelService;
import com.lanchetech.user.config.ReturnPayInfoVO;
import com.lanchetech.user.service.AnchorService;
import com.lanchetech.user.service.CommonService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class AnchorServiceImpl implements AnchorService {

    private final Logger log = LoggerFactory.getLogger(this.getClass());

    @Autowired
    WxPayService wxPayService;

    @Autowired
    AnchorDepositMapper anchorDepositMapper;

    @Resource
    UidGenerator uidGenerator;

    @Value("${domain.url}")
    private String domainUrl;

    @Autowired
    CommonService commonService;

    @Autowired
    LevelService levelService;

    @Autowired
    IdentityMapper identityMapper;

    @Autowired
    UserMapper userMapper;

    @Autowired
    UserAddressMapper userAddressMapper;

    @Autowired
    UserInfoMapper userInfoMapper;

    @Autowired
    RoomMapper roomMapper;

    @Autowired
    OrderSkuMapper orderSkuMapper;

    @Autowired
    DaoService daoService;

    @Autowired
    AnchorWindowsMapper anchorWindowsMapper;

    @Autowired
    SkuMapper skuMapper;

    @Autowired
    SpuMapper spuMapper;

    @Autowired
    BlogGoodsMapper blogGoodsMapper;

    @Autowired
    AnchorAttentionMapper anchorAttentionMapper;

    @Value("${wx.pay.appId}")
    private String miniAppId;

    @Value("${wx.app.appId}")
    private String appAppId;

    @Override
    public ResultData<ReturnPayInfoVO> createAnchorDeposit(User user, Byte type, Long userAddressId, HttpServletRequest httpServletRequest) {
        // TODO 创建主播保证金要有payWay，支付方式参数

        AnchorDeposit anchorDeposit = AnchorDeposit.builder()
                .anchorUserId(user.getId())
                //todo 测试
                //.amount(ANCHOR_DEPOSIT)
                .amount(new BigDecimal("0.01"))
                .refund(BigDecimal.ZERO)
                .tradeNo(Long.toString(uidGenerator.getUID()))
                .payWay(type)
                .payStatus(AnchorDepositPayStatusEnum.NOT_PAY.getStatus())
                .refundStatus(AnchorDepositRefundStatusEnum.NOT_REFUND.getStatus())
                .createdAt(new Date())
                .updatedAt(new Date())
                //todo 默认1
                .userAddressId(3L)
                .build();
        anchorDepositMapper.insert(anchorDeposit);

        // 创建微信支付
        final WxPayUnifiedOrderRequest wxPayUnifiedOrderRequest = WxPayUnifiedOrderRequest.newBuilder()
                .openid(PayTypeEnum.WECHAT.getType().equals(type) ? user.getOpenid() : null)
                .outTradeNo(anchorDeposit.getTradeNo())
                //订单金额, 单位：分
                .totalFee(anchorDeposit.getAmount().multiply(new BigDecimal(100)).intValue())
                .body("主播保证金")
                .spbillCreateIp(IpUtil.getIpAddr(httpServletRequest))
                .notifyUrl(domainUrl + "/api/anchor/depositWechatPayNotify")
                .build();

        if (PayTypeEnum.WECHAT.getType().equals(anchorDeposit.getPayWay())) {
            wxPayUnifiedOrderRequest.setAppid(miniAppId);
            wxPayUnifiedOrderRequest.setTradeType("JSAPI");
        } else {
            wxPayUnifiedOrderRequest.setAppid(appAppId);
            wxPayUnifiedOrderRequest.setTradeType("APP");
        }

        return new ResultData<>(commonService.getReturnPayInfoVO(wxPayUnifiedOrderRequest));
    }

    @Override
    public String depositWechatPayNotify(String xmlData) {
        try {
            this.wxPayService.getConfig().setAppId(xmlData.indexOf(miniAppId) > -1 ? miniAppId : appAppId);
            this.wxPayService.getConfig().setTradeType(xmlData.indexOf(miniAppId) > -1 ? "JSAPI" : "APP");
            final WxPayOrderNotifyResult notifyResult = this.wxPayService.parseOrderNotifyResult(xmlData);
            // 检查微信支付成功的回调是否是他人伪造
            notifyResult.checkResult(this.wxPayService, "MD5", true);
            log.info("订单号：{}, 主播保证金微信支付成功！", notifyResult.getOutTradeNo());

            AnchorDeposit anchorDeposit = anchorDepositMapper.findOneByTradeNo(notifyResult.getOutTradeNo());

            User user = userMapper.selectByPrimaryKey(anchorDeposit.getAnchorUserId());

            // 发放会员身份
            levelService.addOneMember(user, IsBuyerEnum.BUYER.getType(), MemberStateEnum.SUPER.getType());

            // 找到此人的身份，并设置为主播
            Identity identity = identityMapper.findOneByUserId(user.getId());
            if (identity != null && AnchorLevelEnum.NOT_ANCHOR.getLevel().equals(identity.getAnchorLevel())) {
                identity.setAnchorLevel(AnchorLevelEnum.ONE.getLevel());
                identity.setAnchorTime(new Date());

                // 设置主播归属的区域
                UserAddress userAddress = userAddressMapper.selectByPrimaryKey(anchorDeposit.getUserAddressId());
                identity.setProvince(userAddress.getProvince());
                identity.setCity(userAddress.getCity());
                identity.setArea(userAddress.getArea());
                identityMapper.updateByPrimaryKeySelective(identity);

                //更新userInfo
                UserInfo userInfo = userInfoMapper.findOneByUserId(user.getId());
                userInfo.setAnchorLevel(AnchorLevelEnum.ONE.getLevel());
                userInfo.setAnchorTime(new Date());
                userInfo.setDescription("当前主播暂无描述");
                userInfo.setProvinceCode(userAddress.getProvince());
                userInfo.setCityCode(userAddress.getCity());
                userInfo.setAreaCode(userAddress.getArea());
                userInfoMapper.updateByPrimaryKey(userInfo);

                //更新anchor_deposit
                anchorDeposit.setUpdatedAt(new Date());
                anchorDeposit.setPayStatus(AnchorDepositPayStatusEnum.PAID.getStatus());
                anchorDepositMapper.updateByPrimaryKey(anchorDeposit);
            }

            return WxPayNotifyResponse.success("回调成功！");
        } catch (WxPayException e) {
            e.printStackTrace();
            log.error("主播保证金微信支付失败！");
            return WxPayNotifyResponse.fail("回调有误!");
        }
    }

    @Override
    public ResultData<BasePageResp<AnchorDepositVO>> getAnchorDepositPage(DepositReq req) {
        User user = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        req.setAnchorUserId(user.getId());
        Page page = PageHelper.startPage(req.getPageNo(), req.getPageSize());
        List<AnchorDepositVO> list = anchorDepositMapper.findAllPage(req);
        return PageHelpUtil.buildPage(list, page);
    }

    @Override
    public BaseResp refundDeposit(Long id) {
        User user = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        AnchorDeposit anchorDeposit = anchorDepositMapper.selectByPrimaryKey(id);
        if (anchorDeposit != null && user.getId().equals(anchorDeposit.getAnchorUserId()) && AnchorDepositPayStatusEnum.PAID.getStatus().equals(anchorDeposit.getPayStatus())) {
            anchorDepositMapper.updateRefundStatusById(AnchorDepositRefundStatusEnum.APPLY_REFUND.getStatus(), id);
        }
        return new BaseResp();
    }

    @Override
    public ResultData<UserInfoVO> getAnchorHome(Long userId, User currentUser) {
        if (userId == null) {
            userId = currentUser.getId();
        }
        UserInfo userInfo = userInfoMapper.findOneByUserId(userId);
        UserInfoVO vo = new UserInfoVO();
        User user = userMapper.selectByPrimaryKey(userId);
        BeanUtils.copyProperties(userInfo, vo);
        vo.setUserAvatar(user.getAvatar());
        vo.setUserNickname(user.getNickname());
        vo.setIsLive(roomMapper.findOneByAnchorUserIdAndStatus(userId, RoomStatusEnum.OPEN.getStatus()) != null);
        Long roomGoods = roomMapper.getGoodsCount(userId);
        if (roomGoods == null) {
            roomGoods = 0L;
        }
        Long blogGoods = blogGoodsMapper.countByUserId(userId);
        if (blogGoods == null) {
            blogGoods = 0L;
        }
        vo.setGoodsCount(roomGoods + blogGoods);
        vo.setIsAttention(anchorAttentionMapper.findOneByUserIdAndAnchorUserId(currentUser.getId(), userId) != null);
        vo.setWindowsCount(anchorWindowsMapper.countByAnchorId(userId));
        return new ResultData<>(vo);
    }

    @Override
    public BaseResp editAnchorDescription(String description, User user) {
        userInfoMapper.updateDescriptionByUserId(description, user.getId());
        return new BaseResp();
    }

    @Override
    public ResultData<ProductWindowsVO> getProductWindow(Long userId) {
        ProductWindowsVO vo = new ProductWindowsVO();
        User user = userMapper.selectByPrimaryKey(userId);
        UserInfo userInfo = userInfoMapper.findOneByUserId(userId);
        vo.setUserAvatar(user.getAvatar());
        vo.setUserNickname(user.getNickname());
        vo.setAnchorLevel(userInfo.getAnchorLevel());
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(new Date());
        vo.setAmount(orderSkuMapper.getTodayAmount(userId, getFirstTime(calendar).getTime()));
        vo.setOrderCount(orderSkuMapper.getTodayOrderCount(userId, getFirstTime(calendar).getTime()));
        vo.setPeopleCount(orderSkuMapper.getTodayPeopleCount(userId, getFirstTime(calendar).getTime()));
        return new ResultData<>(vo);
    }

    private Calendar getFirstTime(Calendar calendar) {
        calendar.set(Calendar.MILLISECOND, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        return calendar;
    }

    @Override
    public ResultData<List<AnchorRankVO>> getAnchorRank(AnchorRankReq req) {
        Byte type = req.getType();
        List<AnchorRankVO> voList = new ArrayList<>();
        if (AnchorRankTypeEnum.CARRY_PRODUCT.getType().equals(type)) {
            voList = orderSkuMapper.getCarryProductRank();
        }
        if (AnchorRankTypeEnum.POPULARITY.getType().equals(type)) {
            voList = userInfoMapper.getPopularityRank();
        }
        if (AnchorRankTypeEnum.AREA.getType().equals(type)) {
            voList = userInfoMapper.getAreaRank(req);
        }
        if (!CollectionUtils.isEmpty(voList)) {
            List<Room> openRoomList = roomMapper.findAllByAnchorUserIdsAndStatus(voList.stream().map(AnchorRankVO::getUserId).collect(Collectors.toList()), RoomStatusEnum.OPEN.getStatus());
            Map<Long, Room> openRoomMap = new HashMap<>();
            if (!CollectionUtils.isEmpty(openRoomList)) {
                openRoomList.stream().forEach(item -> {
                    openRoomMap.put(item.getAnchorUserId(), item);
                });
            }
            Map<Long, User> userMap = daoService.getUserMap(voList.stream().map(AnchorRankVO::getUserId).collect(Collectors.toList()));
            voList.stream().forEach(item -> {
                item.setUserAvatar(userMap.get(item.getUserId()).getAvatar());
                item.setUserNickname(userMap.get(item.getUserId()).getNickname());
                item.setIsLive(openRoomMap.containsKey(item.getUserId()));
            });
        }

        return new ResultData<>(voList);
    }

    @Override
    public ResultData<AnchorRankVO> getCurrentAnchorRank(AnchorRankReq req) {
        User user = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        Byte type = req.getType();
        AnchorRankVO vo = new AnchorRankVO();
        if (AnchorRankTypeEnum.CARRY_PRODUCT.getType().equals(type)) {
            //todo 暂用统计的方式获取带货排行
            vo = orderSkuMapper.sumAnchorCarryAmount(user.getId());
            if (vo != null) {
                vo.setRankNumber(orderSkuMapper.getCarryRankNumber(vo.getTotalCount()) + 1);
            }
        }
        if (AnchorRankTypeEnum.POPULARITY.getType().equals(type)) {
            UserInfo userinfo = userInfoMapper.findOneByUserId(user.getId());
            vo.setFansCount(userinfo.getFansCount());
            Long number = userInfoMapper.getPopularityRankNumber(userinfo.getFansCount());
            vo.setRankNumber(number == null ? 1 : number + 1);
        }
        if (AnchorRankTypeEnum.AREA.getType().equals(type)) {
            UserInfo userinfo = userInfoMapper.findOneByUserId(user.getId());
            vo.setFansCount(userinfo.getFansCount());
            Long number = userInfoMapper.getAreaRankNumber(req, userinfo.getFansCount());
            vo.setRankNumber(number == null ? 1 : number + 1);
        }
        return new ResultData<>(vo);
    }

    @Override
    public ResultData<BasePageResp<AnchorWindowsVO>> getAnchorWindowsPage(AnchorWindowsReq req, User user) {
        Page page = PageHelper.startPage(req.getPageNo(), req.getPageSize());
        if (!user.getId().equals(req.getAnchorId())) {
            req.setHidden(ToggleEnum.OFF.getStatus());
        }
        List<AnchorWindowsVO> list = anchorWindowsMapper.findAllPage(req);
        if (!CollectionUtils.isEmpty(list)) {
            List<Long> spuIds = list.stream().map(AnchorWindowsVO::getSpuId).collect(Collectors.toList());
            Map<Long, Spu> spuMap = daoService.getSpuMap(spuIds);

            List<Sku> skuList = skuMapper.findAllBySpuIds(spuIds);
            Map<Long, Long> spuSalesMap = new HashMap<>();
            skuList.stream().forEach(item -> {
                if (spuSalesMap.containsKey(item.getSpuId())) {
                    spuSalesMap.put(item.getSpuId(), spuSalesMap.get(item.getSpuId()) + item.getSales());
                } else {
                    spuSalesMap.put(item.getSpuId(), Long.valueOf(item.getSales()));
                }
            });

            list.stream().forEach(item -> {
                item.setSpu(spuMap.get(item.getSpuId()));
                item.setSale(spuSalesMap.get(item.getSpuId()));
            });
        }
        return PageHelpUtil.buildPage(list, page);
    }

    @Override
    public BaseResp editAnchorWindows(AnchorWindows req) {
        User user = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        Identity identity = identityMapper.findOneByUserId(user.getId());
        BaseResp baseResp = new BaseResp();
        if (identity == null) {
            baseResp.setResultCode(ResultCode.FAIL);
            return baseResp;
        }
//        if (IdentityTypeEnum.PARTNER.getType().equals(identity.getType())
//                && identity.getPartnerExpireTime() != null
//                && identity.getPartnerExpireTime().after(new Date())) {
        if (identity.getPartnerExpireTime() != null && identity.getPartnerExpireTime().after(new Date())) {
            if (req.getId() == null) {
                AnchorWindows selectAnchorWindows = anchorWindowsMapper.findOneBySpuIdAndAnchorId(req.getSpuId(), user.getId());
                if (selectAnchorWindows != null) {
                    baseResp.setResultCode(ResultCode.SPU_WINDOWS_EXIST);
                    baseResp.setObject(selectAnchorWindows);
                    return baseResp;
                }

                Spu spu = spuMapper.selectByPrimaryKey(req.getSpuId());
                if (spu != null) {
                    anchorWindowsMapper.insert(AnchorWindows.builder()
                            .anchorId(user.getId())
                            .spuId(req.getSpuId())
                            .price(spu.getMinPrice())
                            .hidden(ToggleEnum.OFF.getStatus())
                            .sorts(req.getSorts() != null ? req.getSorts() : 0)
                            .name(spu.getName())
                            .createdAt(new Date())
                            .updatedAt(new Date())
                            .build());
                }
            } else {
                AnchorWindows anchorWindows = anchorWindowsMapper.selectByPrimaryKey(req.getId());
                if (anchorWindows.getAnchorId().equals(user.getId())) {
                    if (req.getSpuId() != null) {
                        Spu spu = spuMapper.selectByPrimaryKey(req.getSpuId());
                        anchorWindows.setSpuId(req.getSpuId());
                        anchorWindows.setPrice(spu.getMinPrice());
                        anchorWindows.setName(spu.getName());
                    }
                    anchorWindows.setSorts(req.getSorts());
                    anchorWindows.setUpdatedAt(new Date());
                    anchorWindowsMapper.updateByPrimaryKey(anchorWindows);
                }
                baseResp.setObject(anchorWindows);
            }
        } else {
            baseResp.setResultCode(ResultCode.NOT_PARTNER);
        }
        return baseResp;
    }

    @Override
    public BaseResp deleteAnchorWindows(Long id, Long userId) {
        AnchorWindows anchorWindows = anchorWindowsMapper.findOneBySpuIdAndAnchorId(id, userId);
        User user = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        if (!user.getId().equals(anchorWindows.getAnchorId())) {
            return new BaseResp(ResultCode.EDIT_WINDOWS_FAIL);
        }
        anchorWindowsMapper.deleteByPrimaryKey(id, userId);
        return new BaseResp();
    }

    @Override
    public BaseResp editAnchor(Long id, Byte hidden) {
        AnchorWindows anchorWindows = anchorWindowsMapper.selectByPrimaryKey(id);
        User user = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        if (!anchorWindows.getAnchorId().equals(user.getId())) {
            return new BaseResp(ResultCode.EDIT_WINDOWS_FAIL);
        }
        anchorWindowsMapper.updateHiddenById(hidden, id);
        return new BaseResp();
    }

    @Override
    public ResultData<AnchorDepositResp> paidAnchorDeposit(User user) {
        AnchorDepositResp resp = new AnchorDepositResp();
        AnchorDeposit anchorDeposit = anchorDepositMapper.findOneByAnchorUserIdAndPayStatus(user.getId());
        Identity identity = identityMapper.findOneByUserId(user.getId());
        if (identity != null) {
            resp.setAnchorLevel(identity.getAnchorLevel());
        }
        if (anchorDeposit == null) {
            return new ResultData(resp);
        }
        resp.setAnchorDepositId(anchorDeposit.getId());
        resp.setAmount(anchorDeposit.getAmount());
        resp.setRefundStatus(anchorDeposit.getRefundStatus());
        return new ResultData<>(resp);
    }

    @Override
    public ResultData<BasePageResp<PartnerResp>> getPartnerPage(PartnerReq req) {
        if (!StringUtils.isEmpty(req.getNickname())) {
            List<Long> userIds = userMapper.findIdByNicknameLikeAndLevel(req.getNickname(), UserLevelEnum.PARTNER_L1.getLevel());
            if (CollectionUtils.isEmpty(userIds)) {
                userIds.add(0L);
            }
            req.setUserIds(userIds);
        }
        Page page = PageHelper.startPage(req.getPageNo(), req.getPageSize());
        List<PartnerResp> list = identityMapper.findAllPartnerPage(req);
        if (!CollectionUtils.isEmpty(list)) {
            List<Long> userIds = list.stream().map(PartnerResp::getUserId).collect(Collectors.toList());
            Map<Long, User> userMap = daoService.getUserMap(userIds);

            //销量业绩
            List<PartnerStatisticsVO> statisticsVOList = orderSkuMapper.getPartnerStatistics(userIds);
            Map<Long, PartnerStatisticsVO> partnerStatisticsVOMap = new HashMap<>();
            if (!CollectionUtils.isEmpty(statisticsVOList)) {
                statisticsVOList.stream().forEach(item -> {
                    partnerStatisticsVOMap.put(item.getUserId(), item);
                });
            }

            list.stream().forEach(item -> {
                if (userMap.containsKey(item.getUserId())) {
                    User user = userMap.get(item.getUserId());
                    item.setNickname(user.getNickname());
                    item.setAvatar(user.getAvatar());
                }
                if (partnerStatisticsVOMap.containsKey(item.getUserId())) {
                    PartnerStatisticsVO partnerStatisticsVO = partnerStatisticsVOMap.get(item.getUserId());
                    item.setSaleCount(partnerStatisticsVO.getCount());
                    item.setSaleAmount(partnerStatisticsVO.getAmount());
                }
            });
        }
        return PageHelpUtil.buildPage(list, page);
    }
}